package com.unitd.frame.sso.common.config;

import com.unitd.frame.comm.utils.EnvUtils;
import com.unitd.frame.sso.service.auth.ISSOAuthorization;
import com.unitd.frame.sso.service.cache.ISSOCache;
import com.unitd.frame.sso.service.plugin.AbstractSSOPlugin;
import com.unitd.frame.sso.service.statistic.ISSOStatistic;
import com.unitd.frame.sso.common.helper.SSOReflectHelper;
import com.unitd.frame.sso.service.encrypt.ISSOEncrypt;
import com.unitd.frame.sso.service.parser.ISSOParser;
import com.unitd.frame.sso.common.token.Token;
import com.unitd.frame.sso.exception.SsoException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.util.List;
import java.util.Properties;

/**
 * @desc SSO配置文件对应实体对象,用于定义、解析sso.properties配置文件
 * 按照不同的运行模式启用相应的配置 默认为开发环境;可以在IDE中的VM 参数中加上 -Dsso.run.mode=dev_mode
 * 例如：
 * ------------------------------------------------------------------
 * sso.login.url_online_mode=http://sso.online.com/login.html
 * sso.login.url_test_mode=http://sso.test.com/login.html
 * sso.login.url_dev_mode=http://localhost:8080/login.html
 * ------------------------------------------------------------------
 * @filename SSOConfig.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2016/10/06
 */
public class SSOConfig {

	protected static final Logger logger = LoggerFactory.getLogger(SSOConfig.class.getName());

	private static SSOConfig SSO_CONFIG = null; 								// Properties 配置文件配置对象
	private static final String SSO_ENCODING = EnvUtils.SYS_DEF_ENCODING; 		// 系统默认编码方式
	private static final String SSO_RUN_MODE = "sso.run.mode"; 					// SSO的运行模式key(其值有:dev_mode:开发模式;test_mode:测试模式;online_mode:生产模式, 默认生产模式)

	public static final Charset CHARSET_ENCODING =
			Charset.forName(getSSOEncoding()); 									// 当前SSO的编码格式类型
	public static final String SSO_TOKEN_ATTR = "SSOTokenAttr"; 				// 拦截器判断后设置 Token至当前请求,减少Token解密次数; 在使用 request.setAttribute("ssotoken_attr", token); 设置token后; 使用SOHelper.attrToken(request); 方式获取token
	public static final String SSO_KICK_FLAG = "SSOKickFlag"; 					// 踢出用户逻辑标记
	public static final String SSO_KICK_USER = "SSOKickUser"; 					// 踢出用户
	public static final String SSO_COOKIE_MAXAGE = "sso_cookie_maxage"; 		// SSO 动态设置 Cookie 参数; -1 浏览器关闭时自动删除; 0 立即删除; 120 表示Cookie有效期2分钟(以秒为单位)
	public static final String CUT_SYMBOL = "#"; 								// 自定义的系统分隔符
	private static List<AbstractSSOPlugin> SSO_PLUGIN_LIST ; 					// 自定义SSO插件列表(默认为空)

	private SSOProperties properties = null; 									// Properties 配置文件属性对象
	private String appName = ""; 												// app名称
	private String encoding = SSO_ENCODING; 									// 系统默认编码方式(UTF-8)
	private String runMode = "online_mode"; 									// 默认生成环境的运行模式
	private String secretkey = "123456789098745632"; 							// 盐值加密的18位私钥(当sso.properties中没有配置时,取此值)
	private String cookieName = "uid"; 											// 普通非跨域访问的cookie名称,默认为UID
	private String cookieDomain = ""; 											// 普通非跨域访问的cookie的默认顶级域名
	private String cookiePath = "/"; 											// cookie的默认路径,比如服务器为tomcat,即Cookie在omcat的目录下都有效
	private boolean cookieSecure = false; 										// 创建/使用cookie时,是否只能在HTTPS连接中被浏览器传递到服务器端进行会话验证,如果是HTTP连接则不会传递该信息,所以绝对不会被窃听到(它与cookie的setSecure()方法关联)
	private boolean cookieHttponly = true; 										// 设置创建的cookie默认为httpOnly的,这种情况下,可以使cookie不能通过客户端脚本方法,增加cookie的安全性,防止或者减少XSS攻击及影响
	private int cookieMaxage = -1; 												// cookie的默认有效期; 默认值为-1：表示关闭浏览器cookie就会消失
	private boolean cookieBrowser = true; 										// Cookie 是否开启浏览器版本校验
	private boolean cookieCheckip = false; 										// Cookie是否开启IP校验,默认为false
	private String loginUrl = ""; 												// 默认的登录地址
	private String logoutUrl = ""; 												// 默认的登出地址
	private String paramReturl = "ReturnURL"; 									// 访问SSO的登陆地址时,默认的回调访问url的前缀

	private String authCookieSecretkey = "1234567890XXXXXXXX"; 					// 跨域访问的盐值加密的18位私钥(当sso.properties中没有配置时,取此值)
	private String authCookieName = "pid"; 										// 跨域访问的cookie名称,默认为PID
	private int authCookieMaxage = 180; 										// 跨域访问的cookie有效期,默认为180秒
	private int cacheExpires = -1; 												// cookie的默认效期(-1 浏览器关闭时自动删除; 0 立即删除;120 表示Cookie有效期2分钟(以秒为单位))

	private String tokenClass = null; 											// 自定义的Token类
	private String encryptAlgorithm = null;  									// 对称加密算法（默认 RC4）
	private boolean permissionUri = false; 										// 权限认证（默认 false）
	private ISSOAuthorization authorization;

	/**
	 * @desc 构造方法,初始化sso配置对象
	 */
	public SSOConfig() {
		/* 支持 setInstance 设置初始化 */
	}

	public SSOConfig(SSOProperties properties) {
		this.properties = properties;
	}

	/**
	 * @desc new 当前对象
	 * @return SSOConfig {@link SSOConfig}
	 */
	public static SSOConfig getInstance() {
		if (SSO_CONFIG == null) {
			SSO_CONFIG = new SSOConfig();
		}
		return SSO_CONFIG;
	}

	/**
	 * @desc 设置初始化(可动态修改配置内容)
	 * @param ssoConfig SS0配置对象
	 */
	public synchronized static void setInstance(SSOConfig ssoConfig) {
		if (ssoConfig == null) {
			throw new SsoException("设置初始化SSO配置对象失败,ssoConfig对象为空.");
		} else {
			SSO_CONFIG = ssoConfig;
			logger.info("加载SSO配置对象SSOConfig成功!");
		}
	}

	/**
	 * @desc 初始化SSO资源文件
	 * @param ssoConfig sso配置对象
	 */
	public static SSOConfig init(SSOConfig ssoConfig) {
		SSO_CONFIG = ssoConfig;
		return ssoConfig;
	}

	/**
	 * @desc 初始化SSO资源文件
	 * @param props Properties属性对象
	 */
	protected synchronized void initProperties(Properties props) {
		if (props != null) {
			setInstance(new SSOConfig(new SSOProperties(props, SSO_RUN_MODE, this.getRunMode())));
		} else {
			throw new SsoException("Properties属性对象为空,不能加载和初始化ssoConfig配置对象!");
		}
	}

	/**
	 * @desc 获取当前SSO的编码格式
	 * @return ssoEncoding
	 */
	public static String getSSOEncoding() {
		if (SSO_CONFIG == null) {
			return SSO_ENCODING;
		}
		return getInstance().getEncoding();
	}

	/**
	 * @desc 获取SSO配置属性对象
	 * @return {@link SSOProperties}
	 */
	public static SSOProperties getSSOProperties() {
		return SSO_CONFIG.getProperties();
	}

	/**
	 * @desc 获取SSO配置属性对象
	 * @return SSOProperties
	 */
	public SSOProperties getProperties() {
		return properties;
	}

	public void setProperties(SSOProperties properties) {
		this.properties = properties;
	}

	/**
	 * @desc 获取当前环境中 SSO 的默认运行模式
	 * dev_mode 开发模式 test_mode 测试模式 online_mode 生产模式
	 * @return runMode
	 */
	public String getRunMode() {
		if (properties == null) {
			return runMode;
		}
		return properties.get(SSO_RUN_MODE, runMode);
	}
	public void setRunMode(String runMode) {
		this.runMode = runMode;
	}

	/**
	 * @desc 系统角色(默认 空)
	 * 该属性为跨域区分当前系统使用,与 token 变量应用系统 app 参数自动设置为该属性值.
	 * 该值非空则自动开启跨域功能,单点登录系统名必须设置为 sso.app.name=sso
	 * @return appName
	 */
	public String getAppName() {
		if (properties == null) {
			return appName;
		}
		return properties.get("sso.app.name", appName);
	}

	public void setAppName(String appName) {
		this.appName = appName;
	}

	/**
	 * @desc 编码格式默认 UTF-8
	 * @return encoding
	 */
	public String getEncoding() {
		if (properties == null) {
			return encoding;
		}
		return properties.get("sso.encoding", encoding);
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	/**
	 * @desc 加密时需要使用到的加密盐值
	 * @return secretkey
	 */
	public String getSecretkey() {
		if (properties == null) {
			return secretkey;
		}
		return properties.get("sso.secretkey", secretkey);
	}

	public void setSecretkey(String secretkey) {
		this.secretkey = secretkey;
	}

	/**
	 * @desc 获取Cookie 名称,默认uid
	 * @return cookieName
	 */
	public String getCookieName() {
		if (properties == null) {
			return cookieName;
		}
		return properties.get("sso.cookie.name", cookieName);
	}

	public void setCookieName(String cookieName) {
		this.cookieName = cookieName;
	}

	/**
	 * @desc Cookie 所在域
	 * @return cookieDomain
	 */
	public String getCookieDomain() {
		if (properties == null) {
			return cookieDomain;
		}
		return properties.get("sso.cookie.domain", cookieDomain);
	}

	public void setCookieDomain(String cookieDomain) {
		this.cookieDomain = cookieDomain;
	}

	/**
	 * @desc Cookie 域路径,默认/
	 * @return cookiePath
	 */
	public String getCookiePath() {
		if (properties == null) {
			return cookiePath;
		}
		return properties.get("sso.cookie.path", cookiePath);
	}

	public void setCookiePath(String cookiePath) {
		this.cookiePath = cookiePath;
	}

	/**
	 * @desc Cookie 是否只允许https协议传输,默认false
	 * @return true/false
	 */
	public boolean getCookieSecure() {
		if (properties == null) {
			return cookieSecure;
		}
		return properties.getBoolean("sso.cookie.secure", cookieSecure);
	}

	public void setCookieSecure(boolean cookieSecure) {
		this.cookieSecure = cookieSecure;
	}

	/**
	 * @desc Cookie 只读,不允许 Js访问
	 * @return true/false
	 */
	public boolean getCookieHttponly() {
		if (properties == null) {
			return cookieHttponly;
		}
		return properties.getBoolean("sso.cookie.httponly", cookieHttponly);
	}

	public void setCookieHttponly(boolean cookieHttponly) {
		this.cookieHttponly = cookieHttponly;
	}

	/**
	 * @desc Cookie 超时时间(-1 浏览器关闭时自动删除 0 立即删除 120 表示Cookie有效期2分钟(以秒为单位))
	 * @return int 超时时间
	 */
	public int getCookieMaxage() {
		if (properties == null) {
			return cookieMaxage;
		}
		return properties.getInt("sso.cookie.maxage", cookieMaxage);
	}

	public void setCookieMaxage(int cookieMaxage) {
		this.cookieMaxage = cookieMaxage;
	}

	/**
	 * @desc Cookie 是否开启浏览器版本校验
	 * @return true/false
	 */
	public boolean getCookieBrowser() {
		if (properties == null) {
			return cookieBrowser;
		}
		return properties.getBoolean("sso.cookie.browser", cookieBrowser);
	}

	public void setCookieBrowser(boolean cookieBrowser) {
		this.cookieBrowser = cookieBrowser;
	}

	/**
	 * @desc Cookie 是否开启IP校验
	 * @return true/false
	 */
	public boolean getCookieCheckip() {
		if (properties == null) {
			return cookieCheckip;
		}
		return properties.getBoolean("sso.cookie.checkip", cookieCheckip);
	}

	public void setCookieCheckip(boolean cookieCheckip) {
		this.cookieCheckip = cookieCheckip;
	}

	/**
	 * @desc SSO 登录地址(sso服务登陆地址)
	 * @return String
	 */
	public String getLoginUrl() {
		if (properties == null) {
			return loginUrl;
		}
		return properties.get("sso.login.url", loginUrl);
	}

	public void setLoginUrl(String loginUrl) {
		this.loginUrl = loginUrl;
	}

	/**
	 * @desc SSO 退出地址
	 * @return String
	 */
	public String getLogoutUrl() {
		if (properties == null) {
			return logoutUrl;
		}
		return properties.get("sso.logout.url", logoutUrl);
	}

	public void setLogoutUrl(String logoutUrl) {
		this.logoutUrl = logoutUrl;
	}

	/**
	 * @desc SSO 跳转参数命名(默认:ReturnURL)
	 * @return String
	 */
	public String getParamReturl() {
		if (properties == null) {
			return paramReturl;
		}
		return properties.get("sso.param.returl", paramReturl);
	}

	public void setParamReturl(String paramReturl) {
		this.paramReturl = paramReturl;
	}

	/**
	 * @desc 跨域 AuthCookie 密钥加密盐值
	 * @return 密钥加密盐值
	 */
	public String getAuthCookieSecretkey() {
		if (properties == null) {
			return authCookieSecretkey;
		}
		return properties.get("sso.authcookie.secretkey", authCookieSecretkey);
	}

	public void setAuthCookieSecretkey(String authCookieSecretkey) {
		this.authCookieSecretkey = authCookieSecretkey;
	}

	/**
	 * @desc 跨域 AuthCookie 名称
	 * @return 跨域AuthCookie 名称
	 */
	public String getAuthCookieName() {
		if (properties == null) {
			return authCookieName;
		}
		return properties.get("sso.authcookie.name", authCookieName);
	}

	public void setAuthCookieName(String authCookieName) {
		this.authCookieName = authCookieName;
	}

	/**
	 * @desc 跨域 AuthCookie 超时时间(-1 浏览器关闭时自动删除 0 立即删除 180 表示Cookie有效期3分钟(以秒为单位))
	 * @return int 超时时间
	 */
	public int getAuthCookieMaxage() {
		if (properties == null) {
			return authCookieMaxage;
		}
		return properties.getInt("sso.authcookie.maxage", authCookieMaxage);
	}

	public void setAuthCookieMaxage(int authCookieMaxage) {
		this.authCookieMaxage = authCookieMaxage;
	}

	/**
	 * @desc 自定义的SSO加密工具类(根据不同的算法类型,获取不同的加密工具类;所有自定义的 SSOEncrypt 类都需要实现 ISSOEncrypt 接口)
	 * @return ISSOEncrypt {@link ISSOEncrypt}接口实现类
	 */
	public ISSOEncrypt getEncrypt() {
		if (properties == null) {
			return SSOReflectHelper.getConfigEncrypt(null, this.getEncryptAlgorithm());
		}
		return SSOReflectHelper.getConfigEncrypt(properties.get("sso.encrypt.class", ""), this.getEncryptAlgorithm());
	}

	public void setEncrypt(ISSOEncrypt encrypt) {
		if (encrypt != null) {
			SSOReflectHelper.setConfigEncrypt(encrypt);
		}
	}

	/**
	 * @desc 自定义的SSO解析工具类,所有自定义的 SSOParser 类都需要实现 ISSOParser 接口
	 * @return ISSOParser {@link ISSOParser}接口实现类
	 */
	public ISSOParser getParser() {
		if (properties == null) {
			return SSOReflectHelper.getConfigParser(null);
		}
		return SSOReflectHelper.getConfigParser(properties.get("sso.parser.class", ""));
	}

	public void setParser(ISSOParser parser) {
		if (parser != null) {
			SSOReflectHelper.setConfigParser(parser);
		}
	}

	/**
	 * @desc 自定义扩展 SSOCache 类,所有自定义的 SSOCache 类都需要实现 ISSOCache 接口
	 * @return SSOCache {@link ISSOCache}接口实现类
	 */
	public ISSOCache getCache() {
		if (properties == null) {
			return SSOReflectHelper.getConfigCache(null);
		}
		return SSOReflectHelper.getConfigCache(properties.get("sso.cache.class", ""));
	}

	public void setCache(ISSOCache cache) {
		if (cache != null) {
			SSOReflectHelper.setConfigCache(cache);
		}
	}

	/**
	 * @desc 自定义SSOCache 的有效期
	 * 设置缓存Token 如缓存不存在将自动退出登录
	 * 缓存过期时间,小于0不过期,单位时间 s 秒、
	 * @return 单位秒数
	 */
	public int getCacheExpires() {
		if (properties == null) {
			return cacheExpires;
		}
		return properties.getInt("sso.cache.expires", cacheExpires);
	}

	public void setCacheExpires(int cacheExpires) {
		this.cacheExpires = cacheExpires;
	}

	/**
	 * @desc 自定义的SSO统计类(所有自定义的SSO统计类都需要集成自SSOStatistic)
	 * @return SSOStatistic {@link ISSOStatistic}
	 */
	public ISSOStatistic getStatistic() {
		if (properties == null) {
			return SSOReflectHelper.getConfigStatistic(null);
		}
		return SSOReflectHelper.getConfigStatistic(properties.get("sso.statistic.class", ""));
	}

	public void setStatistic(ISSOStatistic statistic) {
		if (statistic != null) {
			SSOReflectHelper.setConfigStatistic(statistic);
		}
	}

	/**
	 * @desc 自定义Token Class,用于实现自定义业务的Token
	 * @return 自定义Token Class
	 */
	public Token getToken() {
		if (properties == null) {
			return SSOReflectHelper.getConfigToken(tokenClass);
		}
		return SSOReflectHelper.getConfigToken(properties.get("sso.token.class", ""));
	}

	public void setTokenClass(String tokenClass) {
		this.tokenClass = tokenClass;
	}

	/**
	 * @desc SSO 默认使用的加密算法类型(DES,AES,BLOWFISH,RC2,RC4; 默认RC4)
	 * @return String
	 */
	public String getEncryptAlgorithm() {
		if (properties == null) {
			return encryptAlgorithm;
		}
		return properties.get("sso.encrypt.algorithm", encryptAlgorithm);
	}

	public void setEncryptAlgorithm(String encryptAlgorithm) {
		this.encryptAlgorithm = encryptAlgorithm;
	}

	/**
	 * @desc 是否进行URL 权限认证
	 * @return true/false
	 */
	public boolean isPermissionUri() {
		if (properties == null) {
			return permissionUri;
		}
		return properties.getBoolean("sso.permission.uri", permissionUri);
	}

	public void setPermissionUri(boolean permissionUri) {
		this.permissionUri = permissionUri;
	}

	/**
	 * 自定义 SSOAuthorization Class
	 */
	public ISSOAuthorization getAuthorization() {
		if (properties == null) {
			return SSOReflectHelper.getAuthorization(null);
		}
		return SSOReflectHelper.getAuthorization(properties.get("sso.authorization.class", ""));
	}

	public void setAuthorization(ISSOAuthorization authorization) {
		if (authorization != null) {
			SSOReflectHelper.setAuthorization(authorization);
		}
	}

	/**
	 * @desc 自定义插件列表,所有自定义的 SsoPlugin 类都需要继承 AbstractSsoPlugin 抽象类
	 * @return AbstractSsoPlugin {@link AbstractSSOPlugin}
	 */
	public List<AbstractSSOPlugin> getPluginList() {
		return SSO_PLUGIN_LIST;
	}

	public void setPluginList(List<AbstractSSOPlugin> pluginList) {
		SSO_PLUGIN_LIST = pluginList;
	}

	/**
	 * @desc 生成 Token 的缓存主键
	 * @param userId 用户ID
	 * @return Token 的缓存主键
	 */
	public static String toCacheKey(Object userId) {
		StringBuilder sb = new StringBuilder();
		sb.append("ssoTokenKey_").append(userId);
		return sb.toString();
	}
}